/******************************************************************************
  * @project: LT9211
  * @file: lt9211.c
  * @author: zll
  * @company: LONTIUM COPYRIGHT and CONFIDENTIAL
  * @date: 2019.04.10
******************************************************************************/

#include "lt9211.h"
#include "util.h"
#include "printf.h"
#include "mipi_dcs.h"
#include "i2c.h"
#include "systick.h"

uint16_t hact, vact;
uint16_t hs, vs;
uint16_t hbp, vbp;
uint16_t htotal, vtotal;
uint16_t hfp, vfp;
uint8_t VideoFormat = 0;
uint32_t lvds_clk_in = 0;

#define OFFSET 5

struct video_timing *pVideo_Format;

//hfp, hs, hbp, hact, htotal, vfp, vs, vbp, vact, vtotal, pixclk
struct video_timing video_640x480_60Hz = {8, 96, 40, 640, 800, 33, 2, 10, 480, 525, 25000};
struct video_timing video_720x480_60Hz = {16, 62, 60, 720, 858, 9, 6, 30, 480, 525, 27000};
struct video_timing video_800x1280_60Hz = {120, 29, 59, 800, 1008, 2, 2, 8, 1280, 1292, 76000}; //Default
//struct video_timing video_800x1280_60Hz     ={10, 20,  20, 800,   850,  10,  10,  20, 1280,   1320,  66000};  // Tester
//struct video_timing video_800x1280_60Hz     ={120, 29,  59, 800,   1008,  2,  2,  8, 1280,   1292,  46000};  // Tester
struct video_timing video_1280x720_60Hz = {110, 40, 220, 1280, 1650, 5, 5, 20, 720, 750, 74250};
struct video_timing video_1280x720_30Hz = {110, 40, 220, 1280, 1650, 5, 5, 20, 720, 750, 74250};
struct video_timing video_1366x768_60Hz = {26, 110, 110, 1366, 1592, 13, 6, 13, 768, 800, 81000};
struct video_timing video_1920x720_60Hz = {148, 44, 88, 1920, 2200, 28, 5, 12, 720, 765, 88000};
struct video_timing video_1920x1080_30Hz = {88, 44, 148, 1920, 2200, 4, 5, 36, 1080, 1125, 74250};
struct video_timing video_1920x1080_60Hz = {88, 44, 148, 1920, 2200, 4, 5, 36, 1080, 1125, 148500};
struct video_timing video_1920x1200_60Hz = {48, 32, 80, 1920, 2080, 3, 6, 26, 1200, 1235, 154000};
struct video_timing video_3840x2160_30Hz = {176, 88, 296, 3840, 4400, 8, 10, 72, 2160, 2250, 297000};

void LT9211_ChipID(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x81); //register bank
	printf("\r\nLT9211 Chip ID:%x,", HDMI_ReadI2C_Byte(0x00));
	printf("%x,", HDMI_ReadI2C_Byte(0x01));
	printf("%x", HDMI_ReadI2C_Byte(0x02));
}

/** video chk soft rst **/
void lt9211_vid_chk_rst(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x10, 0xbe);
	delay_1ms(10);
	HDMI_WriteI2C_Byte(0x10, 0xfe);
}

/** lvds rx logic rst **/
void lt9211_lvdsrx_logic_rst(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x0c, 0xeb);
	delay_1ms(10);
	HDMI_WriteI2C_Byte(0x0c, 0xfb);
}

void LT9211_SystemInt(void)
{
	/* system clock init */
	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x01, 0x18);

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x06, 0x61);
	HDMI_WriteI2C_Byte(0x07, 0xa8); //fm for sys_clk

	HDMI_WriteI2C_Byte(0xff, 0x87); //��ʼ�� txpll �Ĵ����б�Ĭ��ֵ������
	HDMI_WriteI2C_Byte(0x14, 0x08); //default value
	HDMI_WriteI2C_Byte(0x15, 0x00); //default value
	HDMI_WriteI2C_Byte(0x18, 0x0f);
	HDMI_WriteI2C_Byte(0x22, 0x08); //default value
	HDMI_WriteI2C_Byte(0x23, 0x00); //default value
	HDMI_WriteI2C_Byte(0x26, 0x0f);
}

void LT9211_LvdsRxPhy(void)
{
#ifdef INPUT_PORTA
	printf("\r\nPort A PHY Config");
	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x02, 0x8B); //Port A LVDS mode enable
	HDMI_WriteI2C_Byte(0x05, 0x21); //port A CLK lane swap
	HDMI_WriteI2C_Byte(0x07, 0x1f); //port A clk enable
	HDMI_WriteI2C_Byte(0x04, 0xa0); //select port A clk as byteclk
	//HDMI_WriteI2C_Byte(0x09,0xFC); //port A P/N swap

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x33, 0xe4); //Port A Lane swap
#endif

#ifdef INPUT_PORTB
	printf("\r\nPort B PHY Config");
	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x02, 0x88); //Port A/B LVDS mode enable
	HDMI_WriteI2C_Byte(0x05, 0x21); //port A CLK lane swap and rterm turn-off
	HDMI_WriteI2C_Byte(0x0d, 0x21); //port B CLK lane swap
	HDMI_WriteI2C_Byte(0x07, 0x1f); //port A clk enable  (ֻ��Portbʱ,porta��lane0 clkҪ��)
	HDMI_WriteI2C_Byte(0x0f, 0x1f); //port B clk enable
	//HDMI_WriteI2C_Byte(0x10,0x00);   //select port B clk as byteclk
	HDMI_WriteI2C_Byte(0x04, 0xa1); //reserve
	//HDMI_WriteI2C_Byte(0x11,0x01);   //port B P/N swap
	HDMI_WriteI2C_Byte(0x10, 0xfc);

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x34, 0xe4); //Port B Lane swap

	HDMI_WriteI2C_Byte(0xff, 0xd8);
	HDMI_WriteI2C_Byte(0x16, 0x80);
#endif

	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x20, 0x7f);
	HDMI_WriteI2C_Byte(0x20, 0xff); //mlrx calib reset
}

void LT9211_LvdsRxDigital(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x85);
	HDMI_WriteI2C_Byte(0x88, 0xd0); //LVDS input, MIPI output

	HDMI_WriteI2C_Byte(0xff, 0xd8);

	if (INPUT_PORT_NUM == 1) //1Port LVDS Input
	{
		HDMI_WriteI2C_Byte(0x10, 0x80);
		printf("\r\nLVDS Port Num: 1");
	} else if (INPUT_PORT_NUM == 2) //2Port LVDS Input
	{
		HDMI_WriteI2C_Byte(0x10, 0x00);
		printf("\r\nLVDS Port Num: 2");
	} else {
		printf("\r\nPort Num Set Error");
	}

	lt9211_vid_chk_rst();	   //video chk soft rst
	lt9211_lvdsrx_logic_rst(); //lvds rx logic rst

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x30, 0x45); //port AB input port sel

	if (LVDS_FORMAT == JEDIA_FORMAT) {
		HDMI_WriteI2C_Byte(0xff, 0xd8);
		HDMI_WriteI2C_Byte(0x11, 0x40);
	}
}

int lt9211_lvds_clkstb_check(void)
{
	uint8_t porta_clk_state = 0;
	uint8_t portb_clk_state = 0;

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x00, 0x01);
	delay_1ms(300);
	porta_clk_state = (HDMI_ReadI2C_Byte(0x08) & (0x20));

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x00, 0x02);
	delay_1ms(300);
	portb_clk_state = (HDMI_ReadI2C_Byte(0x08) & (0x20));

	if (INPUT_PORT_NUM == 1) {
#ifdef INPUT_PORTA
		if (porta_clk_state) {
			return 1;
		} else {
			return 0;
		}
#endif
#ifdef INPUT_PORTB
		if (portb_clk_state) {
			return 1;
		} else {
			return 0;
		}
#endif
	} else if (INPUT_PORT_NUM == 2) {
		if (porta_clk_state && portb_clk_state) {
			return 1;
		} else {
			return 0;
		}
	}
}

void LT9211_ClockCheckDebug(void)
{
#ifdef _uart_debug_
	uint32_t fm_value;

	lvds_clk_in = 0;
#ifdef INPUT_PORTA
	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x00, 0x01);
	delay_1ms(50);
	fm_value = 0;
	fm_value = (HDMI_ReadI2C_Byte(0x08) & (0x0f));
	fm_value = (fm_value << 8);
	fm_value = fm_value + HDMI_ReadI2C_Byte(0x09);
	fm_value = (fm_value << 8);
	fm_value = fm_value + HDMI_ReadI2C_Byte(0x0a);
	printf("\r\nPort A lvds clock: ");
	printfdec_uint32_t(fm_value);
	lvds_clk_in = fm_value;
#endif

#ifdef INPUT_PORTB
	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x00, 0x02);
	delay_1ms(50);
	fm_value = 0;
	fm_value = (HDMI_ReadI2C_Byte(0x08) & (0x0f));
	fm_value = (fm_value << 8);
	fm_value = fm_value + HDMI_ReadI2C_Byte(0x09);
	fm_value = (fm_value << 8);
	fm_value = fm_value + HDMI_ReadI2C_Byte(0x0a);
	printf("\r\nPort B lvds clock: ");
	printfdec_uint32_t(fm_value);
	lvds_clk_in = fm_value;
#endif

#endif
}

void LT9211_LvdsClkCheck(void)
{
	uint8_t porta_clk_state = 0;

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x00, 0x01);

	while (1) {
		uint8_t reg08 = HDMI_ReadI2C_Byte(0x08);
		uint8_t reg09 = HDMI_ReadI2C_Byte(0x09);
		uint8_t reg0a = HDMI_ReadI2C_Byte(0x0A);

		printf("reg08:%02x, reg09:%02x, reg0a:%02x\r\n", reg08, reg09, reg0a);

		delay_1ms(50);
		porta_clk_state = (HDMI_ReadI2C_Byte(0x08) & (0x20));
		if (porta_clk_state) {
			break;
		}

		printf("LVDS not ready\r\n");
	}

	printf("LVDS ready!\r\n");
}

void LT9211_LvdsRxPll(void)
{
	uint8_t loopx = 0;

	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x25, 0x05);
	HDMI_WriteI2C_Byte(0x27, 0x02);

	if (INPUT_PORT_NUM == 1) //1Port LVDS Input
	{
		HDMI_WriteI2C_Byte(0x24, 0x24); //RXPLL_LVDSCLK_MUXSEL,PIXCLK_MUXSEL	0x2c.
		HDMI_WriteI2C_Byte(0x28, 0x44); //0x64
	} else if (INPUT_PORT_NUM == 2)		//2Port LVDS Input
	{
		HDMI_WriteI2C_Byte(0x24, 0x2c); //RXPLL_LVDSCLK_MUXSEL,PIXCLK_MUXSEL	0x2c.
		HDMI_WriteI2C_Byte(0x28, 0x64); //0x64
	} else {
		printf("\r\n LvdsRxPll: lvds port count error");
	}
	HDMI_WriteI2C_Byte(0xff, 0x87);
	HDMI_WriteI2C_Byte(0x05, 0x00);
	HDMI_WriteI2C_Byte(0x05, 0x80);
	delay_1ms(100);
	for (loopx = 0; loopx < 10; loopx++) //Check Rx PLL cal
	{
		HDMI_WriteI2C_Byte(0xff, 0x87);
		if (HDMI_ReadI2C_Byte(0x12) & 0x80) {
			if (HDMI_ReadI2C_Byte(0x11) & 0x80) {
				printf("\r\nLT9211 rx cal done");
			} else {
				printf("\r\nLT9211 rx cal undone!!");
			}
			printf("\r\nLT9211 rx pll lock");
			break;
		} else {
			printf("\r\nLT9211 rx pll unlocked");
		}
	}
}

void LT9211_VideoCheck(void)
{
	uint8_t sync_polarity;

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x20, 0x00);

	sync_polarity = HDMI_ReadI2C_Byte(0x70);
	vs = HDMI_ReadI2C_Byte(0x71);

	hs = HDMI_ReadI2C_Byte(0x72);
	hs = (hs << 8) + HDMI_ReadI2C_Byte(0x73);

	vbp = HDMI_ReadI2C_Byte(0x74);
	vfp = HDMI_ReadI2C_Byte(0x75);

	hbp = HDMI_ReadI2C_Byte(0x76);
	hbp = (hbp << 8) + HDMI_ReadI2C_Byte(0x77);

	hfp = HDMI_ReadI2C_Byte(0x78);
	hfp = (hfp << 8) + HDMI_ReadI2C_Byte(0x79);

	vtotal = HDMI_ReadI2C_Byte(0x7A);
	vtotal = (vtotal << 8) + HDMI_ReadI2C_Byte(0x7B);

	htotal = HDMI_ReadI2C_Byte(0x7C);
	htotal = (htotal << 8) + HDMI_ReadI2C_Byte(0x7D);

	vact = HDMI_ReadI2C_Byte(0x7E);
	vact = (vact << 8) + HDMI_ReadI2C_Byte(0x7F);

	hact = HDMI_ReadI2C_Byte(0x80);
	hact = (hact << 8) + HDMI_ReadI2C_Byte(0x81);

	printf("\r\nsync_polarity = %x", sync_polarity);
	if (!(sync_polarity & 0x01)) //hsync
	{
		HDMI_WriteI2C_Byte(0xff, 0xd8);
		HDMI_WriteI2C_Byte(0x10, (HDMI_ReadI2C_Byte(0x10) | 0x10));
	}
	if (!(sync_polarity & 0x02)) //vsync
	{
		HDMI_WriteI2C_Byte(0xff, 0xd8);
		HDMI_WriteI2C_Byte(0x10, (HDMI_ReadI2C_Byte(0x10) | 0x20));
	}

	printf("\r\nhfp, hs, hbp, hact, htotal = %d %d %d %d %d ", hfp, hs, hbp, hact, htotal);
	/*printfdec_uint32_t(hfp);
	printfdec_uint32_t(hs);
	printfdec_uint32_t(hbp);
	printfdec_uint32_t(hact);
	printfdec_uint32_t(htotal);*/

	printf("\r\nvfp, vs, vbp, vact, vtotal = %d %d %d %d %d ", vfp, vs, vbp, vact, vtotal);
	/*printfdec_uint32_t(vfp);
	printfdec_uint32_t(vs);
	printfdec_uint32_t(vbp);
	printfdec_uint32_t(vact);
	printfdec_uint32_t(vtotal);*/

	if ((hact == video_800x1280_60Hz.hact) && (vact == video_800x1280_60Hz.vact)) {
		printf("\r\nvideo_800x1280_60Hz");
		pVideo_Format = &video_800x1280_60Hz;
	} else if ((hact == video_1920x1080_60Hz.hact) && (vact == video_1920x1080_60Hz.vact)) {
		printf("\r\nvideo_1920x1080_60Hz");
		pVideo_Format = &video_1920x1080_60Hz;
	} else {
		pVideo_Format = NULL;
		printf("\r\nvideo_none");
	}
}

void LT9211_MipiTxpll(void)
{
	uint8_t loopx;

	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x36, 0x03); //b7:txpll_pd
	
	
	LCD_Reset();
	
	
	
	HDMI_WriteI2C_Byte(0x37, 0x28);
	HDMI_WriteI2C_Byte(0x38, 0x04);
	HDMI_WriteI2C_Byte(0x3a, 0x92);

	HDMI_WriteI2C_Byte(0xff, 0x87);
	HDMI_WriteI2C_Byte(0x13, 0x00);
	HDMI_WriteI2C_Byte(0x13, 0x80);
	delay_1ms(100);
	for (loopx = 0; loopx < 10; loopx++) //Check Tx PLL cal done
	{

		HDMI_WriteI2C_Byte(0xff, 0x87);
		if (HDMI_ReadI2C_Byte(0x1f) & 0x80) {
			if (HDMI_ReadI2C_Byte(0x20) & 0x80) {
				printf("\r\nLT9211 tx pll lock");
			} else {
				printf("\r\nLT9211 tx pll unlocked");
			}
			printf("\r\nLT9211 tx pll cal done");
			break;
		} else {
			printf("\r\nLT9211 tx pll unlocked");
		}
	}
}

void LT9211_MipiTxPhy(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x62, 0x00); //ttl output disable
	HDMI_WriteI2C_Byte(0x3b, 0x32); //mipi en

	//delay_1ms(50);
	//LCD_Reset();

	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x20, 0xfb);
	delay_1ms(10);
	HDMI_WriteI2C_Byte(0x20, 0xff); //tx rterm calibration

	//HDMI_WriteI2C_Byte(0x48,0x5f); //Port A Lane P/N Swap
	//HDMI_WriteI2C_Byte(0x49,0x92);
	//HDMI_WriteI2C_Byte(0x52,0x5f); //Port B Lane P/N Swap
	//HDMI_WriteI2C_Byte(0x53,0x92);

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x40, 0x80); //tx_src_sel
	/*port src sel*/
	HDMI_WriteI2C_Byte(0x41, 0x01);
	HDMI_WriteI2C_Byte(0x42, 0x23);
	HDMI_WriteI2C_Byte(0x43, 0x40); //Port A MIPI Lane Swap
	HDMI_WriteI2C_Byte(0x44, 0x12);
	HDMI_WriteI2C_Byte(0x45, 0x34); //Port B MIPI Lane Swap
}

void LT9211_MipiTxDigital(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x85);
	HDMI_WriteI2C_Byte(0x88, 0x10); //hs_rqst_pre
	HDMI_WriteI2C_Byte(0xff, 0xd8);
	HDMI_WriteI2C_Byte(0x10, 0xb0); //hs_rqst_pre

	HDMI_WriteI2C_Byte(0xff, 0xd4);
	HDMI_WriteI2C_Byte(0x1c, 0x30); //hs_rqst_pre
	HDMI_WriteI2C_Byte(0x1d, 0x0a); //lpx
	HDMI_WriteI2C_Byte(0x1e, 0x06); //prpr
	HDMI_WriteI2C_Byte(0x1f, 0x0a); //trail
	HDMI_WriteI2C_Byte(0x21, 0x00); //[5]byte_swap,[0]burst_clk

	HDMI_WriteI2C_Byte(0xff, 0xd4);
	HDMI_WriteI2C_Byte(0x16, 0x55);
	HDMI_WriteI2C_Byte(0x10, 0x00);
	HDMI_WriteI2C_Byte(0x11, 0xa0); //read delay
	HDMI_WriteI2C_Byte(0x13, 0x0f); //bit[5:4]:lane num, bit[2]:bllp,bit[1:0]:vid_mode
	HDMI_WriteI2C_Byte(0x14, 0x20); //bit[5:4]:data typ,bit[2:0]:fmt sel 000:rgb888
	HDMI_WriteI2C_Byte(0x21, 0x02);
}

void LT9211_SetTxTiming(void)
{
	uint16_t hact, vact;
	uint16_t hs, vs;
	uint16_t hbp, vbp;
	uint16_t htotal, vtotal;
	uint16_t hfp, vfp;

	pVideo_Format = &video_800x1280_60Hz;

	hact = pVideo_Format->hact;
	vact = pVideo_Format->vact;
	htotal = pVideo_Format->htotal;
	vtotal = pVideo_Format->vtotal;
	hs = pVideo_Format->hs;
	vs = pVideo_Format->vs;
	hfp = pVideo_Format->hfp;
	vfp = pVideo_Format->vfp;
	hbp = pVideo_Format->hbp;
	vbp = pVideo_Format->vbp;

	HDMI_WriteI2C_Byte(0xff, 0xd4);
	HDMI_WriteI2C_Byte(0x04, 0x0e);			//hs[7:0]
	HDMI_WriteI2C_Byte(0x05, 0x0e);			//hbp[7:0]
	HDMI_WriteI2C_Byte(0x06, 0x0e);			//hfp[7:0]
	HDMI_WriteI2C_Byte(0x07, (uint8_t)(hact >> 8)); //hactive[15:8]
	HDMI_WriteI2C_Byte(0x08, (uint8_t)(hact));	//hactive[7:0]

	HDMI_WriteI2C_Byte(0x09, (uint8_t)(vs));	//vfp[7:0]
	HDMI_WriteI2C_Byte(0x0a, 0x00);			//bit[3:0]:vbp[11:8]
	HDMI_WriteI2C_Byte(0x0b, (uint8_t)(vbp));	//vbp[7:0]
	HDMI_WriteI2C_Byte(0x0c, (uint8_t)(vact >> 8)); //vcat[15:8]
	HDMI_WriteI2C_Byte(0x0d, (uint8_t)(vact));	//vcat[7:0]
	HDMI_WriteI2C_Byte(0x0e, (uint8_t)(vfp >> 8));	//vfp[11:8]
	HDMI_WriteI2C_Byte(0x0f, (uint8_t)(vfp));	//vfp[7:0]
}

void LT9211_LVDS2MIPIDSI_Config(void)
{
	printf("\r\n*************LT9211 LVDS2MIPIDSI Config*************");
	LT9211_ChipID();
	LT9211_SystemInt();
	LT9211_LvdsRxPhy();
	LT9211_LvdsRxDigital();
	LT9211_LvdsRxPll();
	//LT9211_LvdsClkCheck();
	//lt9211_lvds_clkstb_check();
	LT9211_VideoCheck();

	if (pVideo_Format != NULL) {
		/********MIPI OUTPUT CONFIG********/
		LT9211_MipiTxPhy();
		LT9211_MipiTxpll();
		LT9211_SetTxTiming();
		InitPanel();
		LT9211_MipiTxDigital();
		printf("LCD initial Success\r\n");
	} else {
		printf("LCD initial Failed\r\n");
	}
}